#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
#include <random>
#include <chrono>
#include "mtree/mtree.hpp"

using namespace std;
using namespace MetricSpaceBenchmark::MetricIndex::MTree;

static long long m_id = 1;

static std::random_device rd;
static std::mt19937_64 gen(rd());
std::uniform_int_distribution<uint64_t> distrib(0, UINT64_MAX);

struct KeyObject {
    std::vector<double> key;

    KeyObject() = default;

    explicit KeyObject(const std::vector<double> &key) : key(key) {}

    KeyObject(const KeyObject &other) : key(other.key) {}

    KeyObject &operator=(const KeyObject &other) {
        if (this != &other) {
            key = other.key;
        }
        return *this;
    }

    [[nodiscard]] double distance(const KeyObject &other) const {
        double dist = 0;
        for (size_t i = 0; i < key.size(); ++i) {
            dist += (key[i] - other.key[i]) * (key[i] - other.key[i]);
        }
        return sqrt(dist);
    }
};

// 从文件中读取数据
void read_data_from_file(const string &filename, vector<Entry<KeyObject>> &entries) {
    ifstream file(filename);
    if (!file.is_open()) {
        cerr << "Failed to open file: " << filename << endl;
        return;
    }

    string line;
    while (getline(file, line)) {
        std::istringstream iss(line);
        std::string value;
        std::vector<double> data;
        while (getline(iss, value, ',')) {
            data.push_back(atof(value.c_str()));
        }

        entries.emplace_back(m_id++, KeyObject(data));
    }

    file.close();
}

int main(int argc, char **argv) {

    std::chrono::steady_clock::time_point time_begin;
    std::chrono::steady_clock::time_point time_end;

    string filename_main = "../../results/mtree/main_out.txt";
    std::ofstream outFile(filename_main, std::ios::app);
    if (outFile.is_open()) {
        outFile << "*******************************Start************************************" << std::endl;
        const int nroutes = 2;
        const int leafcap = 10;

        MTree<KeyObject, nroutes, leafcap> mtree;

        outFile << "Add Points: " << endl;
        vector<Entry<KeyObject>> entries;
        const string filename = "../../dataset/forest_original_100000.txt";
        read_data_from_file(filename, entries);
        outFile << "Point Size: " << entries.size() << ", Add Points Over " << std::endl;

        RoutingObject<KeyObject>::n_build_ops = 0;
        time_begin = std::chrono::steady_clock::now();
        for (const auto &e: entries) {
            mtree.Insert(e);
        }
        time_end = std::chrono::steady_clock::now();

        outFile << "build time:" << std::chrono::duration_cast<std::chrono::milliseconds>(time_end - time_begin).count()
                << " [µs]" << std::endl;
        outFile << " distance ops: " << RoutingObject<KeyObject>::n_build_ops << endl;
        outFile << "************************************************************************" << std::endl;

//        const string cluster_filename = "../../dataset/query_points.txt";
        const string cluster_filename = "../../dataset/query_forest.txt";
        vector<Entry<KeyObject>> cluster;
        read_data_from_file(cluster_filename, cluster);

        double queryRadius = 0.2;
        // 查询平均时间
        unsigned long time = 0.0;

        DBEntry<KeyObject>::n_query_ops = 0;
        for (size_t i = 0; i < cluster.size(); i++) {
            outFile << "Query[" << dec << i << "] " << endl;
            time_begin = std::chrono::steady_clock::now();
            vector<Entry<KeyObject>> results = mtree.RangeQuery(KeyObject(cluster[i].key.key), queryRadius);
            time_end = chrono::steady_clock::now();
            time += chrono::duration_cast<chrono::microseconds>(time_end - time_begin).count();
            // 输出查询结果
            outFile << "该轮范围查询结果数:" << results.size() << ", 执行耗时: "
                    << chrono::duration_cast<chrono::microseconds>(time_end - time_begin).count() << " [µs]"
                    << std::endl;
            results.clear();
        }

        outFile << "************************************************************************" << std::endl;
        time /= cluster.size();
        outFile << "range query avg time is : " << time << " [µs]" << std::endl;
        outFile << "************************************************************************" << std::endl;
        outFile << "avg ops: "
                << (double) DBEntry<KeyObject>::n_query_ops / (double) cluster.size() / (double) entries.size() << endl;

        size_t nBytes = mtree.memory_usage();
        outFile << "bytes in use: " << nBytes << " bytes" << endl;

        outFile << "KNNQuery " << endl;
        std::vector<std::pair<long long, double>> results1 = mtree.KNN_Search(mtree.getMTop(),
                                                                              KeyObject(entries[0].key.key), 10);
        outFile << "Found: " << dec << results1.size() << " entries" << endl;
        for (auto &i: results1) {
            outFile << "    (" << dec << i.first << ") " << hex << i.second << endl;
        }

        mtree.Clear();
        outFile << "********************************Over************************************" << std::endl << std::endl;
    }

    return 0;
}